﻿using System;
using System.Collections.Generic;

namespace LibraryApi.Common
{
    [Serializable]
    internal class HashIndex<T>
    {
        private readonly string m_Name;
        private readonly Func<Book, T> m_indexFunct;
        private readonly Dictionary<T, List< Book>> m_Index;

        public int Count
        {
            get
            {
                return m_Index.Count;
            }
        }

        public HashIndex(string name, Func<Book, T> indexFunct, IEqualityComparer<T> comparerFunct)
        {
            if (string.IsNullOrWhiteSpace(name))
                throw new ArgumentNullException("name");

            if (indexFunct == null)
                throw new ArgumentNullException("indexFunct");

            if (comparerFunct == null)
                throw new ArgumentNullException("comparerFunct");

            m_Name = name;
            m_indexFunct = indexFunct;
            m_Index = new Dictionary<T, List<Book>>(comparerFunct);            
        }

        public void Add(Book book)
        {
            if (book == null)
                throw new ArgumentNullException("book");

            var key = m_indexFunct(book);

            if (m_Index.ContainsKey(key))
                m_Index[key].Add(book);
            else
                m_Index[key] = new List<Book>() { book };
        }

        public void AddRange(IEnumerable<Book> books)
        {
            if (books == null)
                throw new ArgumentNullException("books");

            foreach (var book in books)
                Add(book);
        }

        public void Remove(Book book)
        {
            if (book == null)
                throw new ArgumentNullException("book");

            var key = m_indexFunct(book);

            if (m_Index.ContainsKey(key))
                m_Index[key].Remove(book);
        }

        public void RemoveRange(IEnumerable<Book> books)
        {
            if (books == null)
                throw new ArgumentNullException("books");

            foreach (var book in books)
                Remove(book);
        }

        public IEnumerable<Book> FindByKey(T key)
        {
            if (key == null)
                throw new ArgumentNullException("key");

            if (m_Index.ContainsKey(key))
                foreach (var book in m_Index[key])
                    yield return book;
        }

        public IEnumerable<Book> FindByKey(Func<T, bool> keyFunct)
        {
            if (keyFunct == null)
                throw new ArgumentNullException("keyFunct");

            foreach (var key in m_Index.Keys)
                if (keyFunct(key))
                    foreach (var book in FindByKey(key))
                        yield return book;
        }

        public IEnumerable<Book> FindByValue(Func<Book, bool> valueFunct)
        {
            if (valueFunct == null)
                throw new ArgumentNullException("valueFunct");

            foreach (var key in m_Index.Keys)
                foreach (var book in m_Index[key])
                    if (valueFunct(book))
                        yield return book;
        }

        public void Clear()
        {
            m_Index.Clear();
        }
    }
}
